package com.bc.plugin.doc.builder;

import com.bc.plugin.doc.filter.IControllerFilter;
import com.power.doc.model.ApiConfig;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiMethodDoc;
import com.thoughtworks.qdox.model.JavaClass;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * @author xhh 2019/11/21
 */
public class ApiDocDataBuilder {
    private ApiMethodDocBuilder apiMethodDocBuilder;
    private ApiConfig config;
    private JavaClassFileBuilder javaClassFileBuilder;
    private BuilderConfig builderConfig;

    public ApiDocDataBuilder(ApiConfig config, BuilderConfig builderConfig) {
        this.config = config;
        this.builderConfig = builderConfig;
        this.javaClassFileBuilder = builderConfig.getJavaClassFileBuilder();
        this.apiMethodDocBuilder = new ApiMethodDocBuilder(config, builderConfig);
    }

    /**
     * Get single controller api data by controller fully name.
     *
     * @param controller controller fully name
     * @return ApiDoc
     */
    public ApiDoc getSingleControllerApiData(String controller) {
        if (!javaClassFileBuilder.getJavaFilesMap().containsKey(controller)) {
            throw new RuntimeException("Unable to find " + controller + " in your project");
        }
        JavaClass cls = javaClassFileBuilder.getJavaProjectBuilder().getClassByName(controller);
        if (checkController(null, cls)) {
            String controllerName = cls.getName();
            List<ApiMethodDoc> apiMethodDocs = apiMethodDocBuilder.buildControllerMethod(cls);
            ApiDoc apiDoc = new ApiDoc();
            apiDoc.setList(apiMethodDocs);
            apiDoc.setName(controllerName);
            return apiDoc;
        } else {
            throw new RuntimeException(controller + " is not a Controller in your project");
        }
    }

    /**
     * Get api data
     *
     * @return List of api data
     */
    public List<ApiDoc> getControllerApiData() {
        String packageMatch = config.getPackageFilters();
        Collection<JavaClass> javaClasses = javaClassFileBuilder.getJavaProjectBuilder().getClasses();
        List<ApiDoc> apiDocList = new ArrayList<>();
        String[] packages = packageMatch == null ? null : packageMatch.split(",");
        for (JavaClass cls : javaClasses) {
            if (checkController(packages, cls)) {
                this.handleApiDoc(cls, apiDocList, config);
            }
        }
        return apiDocList;
    }

    private boolean checkController(String[] packages, JavaClass cls) {
        boolean isPackageClass = packages == null;
        if (packages != null) {
            for (String aPackage : packages) {
                if (cls.getCanonicalName().startsWith(aPackage)) {
                    isPackageClass = true;
                }
            }
        }
        if (isPackageClass) {
            for (IControllerFilter controllerFilter : builderConfig.getControllerFilters()) {
                if (controllerFilter.isControllerClass(cls)) {
                    return true;
                }
            }
        }
        return false;
    }


    private void handleApiDoc(JavaClass cls, List<ApiDoc> apiDocList, ApiConfig apiConfig) {
        List<ApiMethodDoc> apiMethodDocs = apiMethodDocBuilder.buildControllerMethod(cls);
        if (apiMethodDocs == null || apiMethodDocs.isEmpty()) {
            return;
        }
        String controllerName = cls.getName();
        ApiDoc apiDoc = new ApiDoc();
        apiDoc.setOrder(apiDocList.size() + 1);
        apiDoc.setName(controllerName);
        apiDoc.setAlias(controllerName);
        this.handControllerAlias(apiDoc, apiConfig);
        String comment = cls.getComment();
        if (StringUtils.isBlank(comment)) {
            comment = null;
            if (apiConfig.isStrict() || apiConfig.isMd5EncryptedHtmlName()) {
                throw new RuntimeException(controllerName + " class not fount comment, you can setStrict(false) and setMd5EncryptedHtmlName(false) to skip this exception");
            }
        }
        comment = Optional.ofNullable(comment).orElse(controllerName);
        apiDoc.setDesc(comment);
        apiDoc.setList(apiMethodDocs);
        apiDocList.add(apiDoc);
    }

    /**
     * handle controller name
     *
     * @param apiDoc ApiDoc
     */
    private void handControllerAlias(ApiDoc apiDoc, ApiConfig apiConfig) {
        if (apiConfig.isMd5EncryptedHtmlName()) {
            String name = DigestUtils.md5Hex(apiDoc.getName());
            int length = name.length();
            if (name.length() < 32) {
                apiDoc.setAlias(name);
            } else {
                apiDoc.setAlias(name.substring(length - 32, length));
            }
        }
    }
}
